{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Notebook Magics"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import holoviews as hv\n",
    "from holoviews import opts\n",
    "hv.extension('bokeh', 'matplotlib')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The [Applying Customization](03-Applying_Customization.ipynb) user guide describes the currently *recommended* way to customize your visualizations in HoloViews. Those mechanisms use standard Python syntax but they are not the only way to apply options as there is a much older approach for working with HoloViews that is specific to notebooks.\n",
    "\n",
    "From the start, HoloViews aimed to enable rapid exploration of data in Jupyter Notebooks. For this reason, when you load the HoloViews extension in a notebook, you also get a set of IPython magics. IPython magics use a syntax that is not standard Python and the HoloViews magics only apply in the notebook environment (and not the IPython terminal for instance).\n",
    "\n",
    "The advantages of the notebook magics are:\n",
    "\n",
    "* They allow tab-completion in the notebook environment (but so do the more recent option builders and `hv.output` mechanisms).\n",
    "* They allow very concise expression of options and settings.\n",
    "\n",
    "\n",
    "Unfortunately, they also have some serious disadvantages:\n",
    "\n",
    "* They are not Python syntax which makes it difficult to use code written with magics in notebooks anywhere else. For instance, it makes it harder to use such code with [bokeh server](https://bokeh.pydata.org/en/latest/docs/reference/server.html) or [panel](http://panel.pyviz.org/).\n",
    "* They have their own special syntax which is very concise but also rather mysterious.\n",
    "\n",
    "\n",
    "These disadvantages means the magics can be bewildering to anyone unfamiliar with the IPython specific syntax and HoloViews itself, and are no longer recommended for these reasons. This user guide documents these magics to allow people to understand older notebooks using HoloViews and to help people update these old notebooks to use the recommended Python API."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Line and cell magics\n",
    "\n",
    "There are two types of magic supported in Jupyter notebooks called *line magics* and *cell magics* respectively. Both typically appear at the top of code cells prefixed by `%` (line magics) or `%%` (cell magics).\n",
    "\n",
    "* **line magics**: These can appear anywhere in a code cell and effect global changes to the current notebook session. HoloViews has the `%opts` and `%output` line magics.\n",
    "* **cell magics**: These have to appear at the top of the cell and are used to modify how that cell is executed. HoloViews has the `%%opts` and `%%ouput` cell magics.\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The %opts and %%opts magics\n",
    "\n",
    "These two magics are now served by `opts.defaults` and the `.opts` method respectively, as described in the [Applying Customization](03-Applying_Customization.ipynb) user guide.\n",
    "\n",
    "* *The ``%opts`` line magic*: IPython specific syntax applied globally *[string format]*\n",
    "* *The ``%%opts`` cell magic*: IPython specific syntax applies to displayed object *[string format]*\n",
    "\n",
    "These magics have their own syntax that separates between *style*, *plot* and *norm* options described towards the end of the [Applying Customization](03-Applying_Customization.ipynb) user guide. The definition of the syntax is as follows:\n",
    "\n",
    "```\n",
    "[[spec] [normalization] [plotting options] [style options]]+\n",
    "\n",
    "spec:             A dotted type.group.label specification\n",
    "                  (e.g. Curve,Sinusoid.Squared)\n",
    "\n",
    "normalization:    List of normalization options delimited by braces.\n",
    "                  One of | -axiswise | -framewise | +axiswise | +framewise |\n",
    "                  E.g. { +axiswise +framewise }\n",
    "\n",
    "plotting options: List of plotting option keywords delimited by\n",
    "                  square brackets. E.g. [show_title=False]\n",
    "\n",
    "style options:    List of style option keywords delimited by\n",
    "                  parentheses. E.g. (lw=10 marker='+')\n",
    "```\n",
    "\n",
    "In other words, you have a list of spec strings (for instance `Curve` or `Curve.Sinusoid`) followed by keywords in either parentheses, square brackets or braces to represent the style, plot and normalization options respectively.\n",
    "\n",
    "## Example `%%opts` cell magic\n",
    "\n",
    "Here is the example from the [Customization](../getting_started/2-Customization.ipynb) section of the 'Getting Started' customized using the `%%opts` cell magic:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%opts Curve  [height=200 width=900 xaxis=None tools=['hover']]\n",
    "%%opts Curve (color='red' line_width=1.5)\n",
    "%%opts Spikes [height=150 width=900 yaxis=None] (color='grey' line_width=0.25)\n",
    "\n",
    "spike_train = pd.read_csv('../assets/spike_train.csv.gz')\n",
    "curve  = hv.Curve( spike_train, 'milliseconds', vdims='Hertz')\n",
    "spikes = hv.Spikes(spike_train, 'milliseconds', vdims=[])\n",
    "layout = (curve+spikes).cols(1)\n",
    "layout"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This `layout` object is now customized in a way that will persist, just like it would using the recommended `.opts` method together with option builders. It is worth noting that instead of just using element names, you can specify the group and label (e.g `Curve.Sinusoid.Squared`) to condition on that metadata, just the way you can using the option builders.\n",
    "\n",
    "\n",
    "## Example `%opts` line magic\n",
    "\n",
    "Here is how you could use the `%opts` line magic instead of `opts.default` as detailed in the [Applying Customization](03-Applying_Customization.ipynb) user guide:\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%opts HeatMap (cmap='Summer') [colorbar=True, toolbar='above']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now all `HeatMap` elements will use the 'Summer' colormap, showing a colorbar with the Bokeh toolbar at the top:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = [(chr(65+i), chr(97+j),  i*j) for i in range(5) for j in range(5) if i!=j]\n",
    "hv.HeatMap(data).sort()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The `%output` line magic\n",
    "\n",
    "The `%output` line magic has been fairly directly replaced by the `hv.output` utility. Here is an example of the `%output` line magic:\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%output backend='matplotlib', fig='svg'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This ensures the following `Path` (and all subsequent `Path` objects) are rendered as SVG with matplotlib:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "lin = np.linspace(0, np.pi*2, 200)\n",
    "\n",
    "def lissajous(t, a, b, delta):\n",
    "    return (np.sin(a * t + delta), np.sin(b * t), t)\n",
    "\n",
    "path = hv.Path([lissajous(lin, 3, 5, np.pi/2)])\n",
    "path.opts(opts.Path(linewidth=2, color='red', linestyle='dotted'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For the purposes of this notebook, let us switch the plotting extension back to bokeh:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%output backend='bokeh'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that the `%output` magic accepts the same set of output settings as the `hv.output` utility."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The `%%output` cell magic\n",
    "\n",
    "If we want to *temporarily* switch to matplotlib with some custom output settings,  we can use the `%%output` cell magic in an example combines the `%%output` and `%%opts` cell magics in the same cell:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%output backend='matplotlib' fig='svg' size=50\n",
    "%%opts Path (linewidth=3 color='blue')\n",
    "lin = np.linspace(0, np.pi*2, 200)\n",
    "\n",
    "def lissajous(t, a, b, delta):\n",
    "    return (np.sin(a * t + delta), np.sin(b * t), t)\n",
    "\n",
    "hv.Path([lissajous(lin, 3, 5, np.pi/2)])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The recommended approach would now be to pass the `path` object to the `hv.output` utility as detailed in the [Applying Customization](03-Applying_Customization.ipynb) user guide. The magic processes the same set of output settings as the `hv.output` utility."
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
